package org.mmtk.plan.scale;

import org.mmtk.plan.Plan;
import org.mmtk.plan.TraceLocal;
import org.mmtk.plan.scale.metrix.ClosureCounts;
import org.mmtk.plan.scale.metrix.DepthClosureCounts;
import org.mmtk.plan.scale.metrix.RunCounts;
import org.mmtk.plan.scale.utils.ScaleLogger;
import org.mmtk.policy.Space;
import org.mmtk.utility.Log;
import org.mmtk.utility.deque.AddressDeque;
import org.mmtk.utility.deque.ObjectReferenceDeque;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.ObjectReference;

@Uninterruptible
public class ScaleTraceLocal extends TraceLocal {
	/****************************************************************************
	 * 
	 * Instance fields
	 */
	protected long invalidTraced;
	
	protected ObjectReferenceDeque toEmpty;	
	protected ObjectReferenceDeque savedValues;
	protected AddressDeque savedRootLocations;
	protected DepthClosureCounts depthClosureCounts;
	protected DepthClosureCounts checkClosureCounts;
	protected ClosureCounts closureCounts;
	protected ClosureCounts currentClosureCounts;
	protected RunCounts runCounts;
	protected boolean initialTrace;
	private String name;
	public String getName() {
		return name;
	}

	/**
	 * Constructor
	 */
	public ScaleTraceLocal(String name, int scan, ScaleTrace scaleTrace) {
		super(scan, scaleTrace);
		this.name = name;
		this.toEmpty = new ObjectReferenceDeque("toEmpty", scaleTrace
				.getToEmptyValuePool());
		this.savedRootLocations = new AddressDeque("savedRoots", scaleTrace
				.getSavedRootLocationPool());
		this.savedValues = new ObjectReferenceDeque("savedValues", scaleTrace
				.getSavedValuePool());
		this.depthClosureCounts = new DepthClosureCounts("metricClosureCounts");
		this.checkClosureCounts = new DepthClosureCounts("checkClosureCounts");
		this.closureCounts = new ClosureCounts("closureCounts");
		this.runCounts = new RunCounts();
	}
	
	public AddressDeque getRoots() {
		return this.rootLocations;
	}
	
	public ObjectReferenceDeque getValues() {
		return this.values;
	}

	/****************************************************************************
	 * 
	 * Externally visible Object processing and tracing, inherited from base
	 */
	/**
	 * Is the specified object live?
	 * 
	 * @param object
	 *            The object.
	 * @return True if the object is live.
	 */
	@Override
	@Inline
	public boolean isLive(ObjectReference object) {
		if (object.isNull())
			return false;
		if (Space.isInSpace(Scale.SCALE_MS_SPACE, object)) {
			return Scale.scaleSpace.isLive(object);
		}
		return super.isLive(object);
	}

	/**
	 * This method is the core method during the trace of the object graph. The
	 * role of this method is to:
	 * 
	 * 1. Ensure the traced object is not collected. 2. If this is the first
	 * visit to the object enqueue it to be scanned. 3. Return the forwarded
	 * reference to the object.
	 * 
	 * In this instance, we refer objects in the mark-sweep space to the msSpace
	 * for tracing, and defer to the superclass for all others.
	 * 
	 * @param object
	 *            The object to be traced.
	 * @return The new reference to the same object instance.
	 */
	@Override
	@Inline
	public ObjectReference traceObject(ObjectReference object) {
		if (object.isNull())
			return object;
		if (Space.isInSpace(Scale.SCALE_MS_SPACE, object))
			return Scale.scaleSpace.traceObject(this, object);
		if (Space.isInSpace(Plan.VM_SPACE, object))
			return (Plan.SCAN_BOOT_IMAGE) ? object : Plan.vmSpace.traceObject(
					this, object);
		if (Space.isInSpace(Plan.IMMORTAL, object))
			return Plan.immortalSpace.traceObject(this, object);
		if (Space.isInSpace(Plan.NON_MOVING, object))
			return Plan.nonMovingSpace.traceObject(this, object);
		if (Plan.USE_CODE_SPACE && Space.isInSpace(Plan.SMALL_CODE, object))
			return Plan.smallCodeSpace.traceObject(this, object);
		if (Space.isInSpace(Plan.LOS, object) || Plan.USE_CODE_SPACE
				&& Space.isInSpace(Plan.LARGE_CODE, object)) {
			if (!this.initialTrace) {
				boolean live = isLive(object);
				if (!live) {
					if (Options.scaleDebug.getValue() > 3) {
						ScaleLogger.acquireLogLock();
						Log.prependThreadId();
						Log.write("Non myms space object reached by a non initial scan should be live !!! ");
						Log.flush();
						VM.objectModel.dumpObject(object);
						Log.flush();
						ScaleLogger.releaseLogLock();
					}
				}
				ObjectReference narrowRef = VM.objectModel.getKathy(object);
				if (!(ObjectReference.fromObject(currentClosureCounts) == narrowRef)) {
					// have not put this object on stack during this closure
					/*
					 * logMessage("Setting LO narrow, space "); Space space =
					 * Space.getSpaceForObject(object); if (space ==
					 * Metrix.myMsSpace) { Log.write(" Myms space "); } else if
					 * (space == Plan.loSpace) { Log.write(" loSpace "); } else
					 * if (space == Plan.nonMovingSpace) {
					 * Log.write(" nonMovingSpace "); } else if
					 * (Plan.USE_CODE_SPACE && space == Plan.smallCodeSpace) {
					 * Log.write(" smallCodeSpace "); } else if
					 * (Plan.USE_CODE_SPACE && space == Plan.largeCodeSpace) {
					 * Log.write(" largeCodeSpace "); } else if (space == null)
					 * { if (VM.VERIFY_ASSERTIONS) {
					 * Log.write("space failure: "); Log.writeln(object); } }
					 * Log.write(" narrow "); Log.writeln(narrowRef);
					 * Log.flush();
					 */
					VM.objectModel.setKathy(object, ObjectReference
							.fromObject(currentClosureCounts));
					// VM.objectModel.dumpObject(object);
					this.processNode(object);
				}
			} else {
				// this is the first trace in a cycle, take care to remove
				// narrow refs
				ObjectReference narrowRef = VM.objectModel.getKathy(object);
				if (ObjectReference.nullReference() != narrowRef) {
					// have not put this object on stack yet
					// Log.writeln("Cleaning narrow in other space object to track it in next closures");
					// Log.flush();
					// VM.objectModel.setNarrow(object,
					// ObjectReference.nullReference());
				}
			}
			if (Space.isInSpace(Plan.LOS, object))
				return Plan.loSpace.traceObject(this, object);
			if (Plan.USE_CODE_SPACE && Space.isInSpace(Plan.LARGE_CODE, object))
				return Plan.largeCodeSpace.traceObject(this, object);
		}
		if (VM.VERIFY_ASSERTIONS) {
			Space.printVMMap();
			logMessage("Failing object => ", object);
			VM.assertions._assert(false,
					"No special case for space in traceObject");
		}
		return ObjectReference.nullReference();
	}

	/**
	 * Overriden to save the roots while processing
	 */
	@Inline
	public void processRoots(boolean save) {
		if (Options.scaleDebug.getValue() > 5) {
			this.logMessage("\tprocessRoots - enter");
		}

		long savedRoots = 0;
		while (!rootLocations.isEmpty()) {
			Address entry = rootLocations.pop();
			if (save) {
				this.savedRootLocations.push(entry);
				savedRoots++;
			}
			processRootEdge(entry, true);
		}
		if (Options.scaleDebug.getValue() > 5) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write("\tprocessRoots - done, saved and processed roots number is ");
			Log.writeln(savedRoots);
			Log.flush();
			ScaleLogger.releaseLogLock();
			printClosureCounts("After processRoots ");		
		}

		if (Options.scaleDebug.getValue() > 5) {
			this.logMessage("\tprocessRoots - exit");
		}
	}

	/**
	 * Overriden to count my space objects pushed to the stack Add a gray object
	 * 
	 * @param object
	 *            The object to be enqueued
	 */
	@Override
	@Inline
	public void processNode(ObjectReference object) {
		if (!VM.objectModel.validRef(object)) {
			this.currentClosureCounts.addIPushed();
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write(" ScaleTraceLocal.processNode !!!invalid: ");
			Log.flush();
			VM.objectModel.dumpObject(object);
			Log.flush();	
			ScaleLogger.releaseLogLock();
			return;
		}
		this.currentClosureCounts.addPushed();
		if (Space.isInSpace(Scale.SCALE_MS_SPACE, object)) {
			if(Options.scaleDebug.getValue() > 7) {
				ScaleLogger.acquireLogLock();
				Log.prependThreadId();
				Log.write(" ScaleTraceLocal.processNode: ");
				Log.flush();
				VM.objectModel.dumpObject(object);
				Log.flush();	
				ScaleLogger.releaseLogLock();
			}
			this.currentClosureCounts.addMsPushed();
		}
		super.processNode(object);
	}

	@Override
	public void prepare() {
		super.prepare();
		setClosureCounts();
		if (Options.scaleDebug.getValue() > 3) {
			// this.closureCounts.dumpObjectCounters("prepare closureCounts");
			// this.metricClosureCounts.dumpObjectCounters("prepare metricClosureCounts");
			// this.fixClosureCounts.dumpObjectCounters("prepare fixClosureCounts");
		}
	}
	

	public void release() {
		super.release();
		this.savedRootLocations.reset();
		this.savedValues.reset();
		this.toEmpty.reset();
		this.runCounts.addCycle(depthClosureCounts,checkClosureCounts);
	}

	/**
	 * Finishing processing all GC work. This method iterates until all work
	 * queues are empty.
	 */
	@Inline
	public void completeTrace(boolean save, boolean cleanNarrow) {
		logMessage("completeTrace - enter");
		if (!rootLocations.isEmpty()) {
			logMessage("completeTrace - will process roots");
			processRoots(save);
		}

		assertMutatorRemsetsFlushed();
		logMessage("completeTrace - will process values");
		ObjectReference v;
		do {
			while (!values.isEmpty()) {
				v = values.pop();
				handlePopped(v, cleanNarrow);
			}
			processRememberedSets();
		} while (!values.isEmpty());
		assertMutatorRemsetsFlushed();
		logMessage("completeTrace - exit");
	}

	public void printClosureCounts(String message) {
		ScaleLogger.acquireLogLock();
		Log.prependThreadId();
		Log.write(name);
		Log.write(": ");
		currentClosureCounts.dumpObjectCounters(message, false);
		ScaleLogger.releaseLogLock();
	}
	
	public void printDepthClosureCounts() {
		ScaleLogger.acquireLogLock();
		Log.write("TotalLive: ");
		Log.writeln(this.depthClosureCounts.getTotalCount());
		this.depthClosureCounts.endClosure();
		ScaleLogger.releaseLogLock();
	}
	
	public void printCheckClosureCounts() {
		ScaleLogger.acquireLogLock();
		this.checkClosureCounts.endClosure();
		ScaleLogger.releaseLogLock();
	}

	void setClosureCounts() {
		if (Options.scaleDebug.getValue() > 5) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write(this.name);
			Log.write(".setClosureCounts, before currentClosureCounts=");
			Log.writeln((currentClosureCounts == null) ? "null": currentClosureCounts.getName());
			ScaleLogger.releaseLogLock();
		}

		this.doSetClosureCounts();

		if (Options.scaleDebug.getValue() > 5) {
			ScaleLogger.acquireLogLock();
			Log.write(this.name);
			Log.write(".setClosureCounts, after currentClosureCounts=");
			Log.writeln((currentClosureCounts == null) ? "null": currentClosureCounts.getName());
			Log.flush();
			ScaleLogger.releaseLogLock();
		}

		currentClosureCounts.init();
	}

	/**
	 * Implemented by subclasses
	 */
	protected void doSetClosureCounts() {
		if (Scale.traceFlavor == Scale.METRIX_TRACE_FLAVOR) {
			if (Scale.closure == Scale.CLOSURE_FIRST_SINGLE) {
				initialTrace = true;
				currentClosureCounts = depthClosureCounts;
			} else if (Scale.closure == Scale.CLOSURE_LAST_SINGLE) {
				initialTrace = false;
				currentClosureCounts = closureCounts;
			} else if (Scale.closure == Scale.CLOSURE_LAST_CONCURRENT) {
				initialTrace = false;
				currentClosureCounts = closureCounts;
			} else if (VM.VERIFY_ASSERTIONS) {
				VM.assertions._assert(false,
				"ScaleTraceLocal.doSetClosureCounts encountered wrong closure");
			}
		} else if (Scale.traceFlavor == Scale.METRIX_TRACE_METRIX_FLAVOR) {
			if (Scale.closure == Scale.CLOSURE_FIRST_SINGLE) {
				initialTrace = true;
				currentClosureCounts = depthClosureCounts;
			} else if (Scale.closure == Scale.CLOSURE_CONCURRENT) {
				initialTrace = false;
				currentClosureCounts = closureCounts;
			} else if (Scale.closure == Scale.CLOSURE_LAST_SINGLE) {
				initialTrace = false;
				currentClosureCounts = this.checkClosureCounts;
			}else if (VM.VERIFY_ASSERTIONS) {
				VM.assertions._assert(false,
				"ScaleTraceLocal.doSetClosureCounts encountered wrong closure");
			}
		} else if (VM.VERIFY_ASSERTIONS) {
			VM.assertions._assert(false,
			"ScaleTraceLocal.doSetClosureCounts encountered wrong trace flavor");
		}
	}

	/*************************************************************************
	 * Depth measuring trace
	 */

	/**
	 * Finishing processing all GC work. This method iterates until all work
	 * queues are empty. Always single threaded - no need to synchronize
	 */
	@Inline
	public void completeMetrixTrace(boolean save, boolean traceNarrow, boolean cleanNarrow) {
		printObjects = false;
		if (Scale.collectorThreads > 1) {
			if (Options.scaleDebug.getValue() > 5) {
				printClosureCounts("completeMetrixTrace before reading initial values");
			}
			currentClosureCounts.addPushed(Scale.getInitialValues());
			currentClosureCounts.addMsPushed(Scale.getInitialMSValues());
			if (Options.scaleDebug.getValue() > 5) {
				printClosureCounts("completeMetrixTrace after reading initial values");
			}
		}

		if (!rootLocations.isEmpty()) {
			if (Options.scaleDebug.getValue() > 5) {
				logMessage("completeMetrixTrace - rootLocations is not empty, calling processRoots");
			}
			processRoots(save);
			if (Options.scaleDebug.getValue() > 5) {
				printClosureCounts("completeMetrixTrace - after processRoots");
			}
		} else {
			if (Options.scaleDebug.getValue() > 5) {
				logMessage("completeMetrixTrace - rootLocations is empty");
			}
		}

		super.assertMutatorRemsetsFlushed();
		do {
			while (!values.isEmpty()) {
				copyValues();
				// printClosureCounts("completeMetrixTrace -  after copying values");
				ObjectReference v;
				ObjectReference narrowRef;
				do {
					while (!toEmpty.isEmpty()) {
						v = toEmpty.pop();				
						if (Options.scaleDebug.getValue() > 5) {
							Log.write("completeMetrixTrace - popped object ");
							Log.write(currentClosureCounts.getMsPopped());
							Log.write(" ");
							Log.write(currentClosureCounts.getMsPushed());
							Log.write(" ");
							Log.write(currentClosureCounts.getDepth());
							Log.write(", space=");	
							Log.write(Space.getSpaceForObject(v).getName());
							Log.write(" ");	
							Log.flush();
							VM.objectModel.dumpObject(v);
							Log.flush();
						}
						currentClosureCounts.addPopped(v);
						if (Space.isInSpace(Scale.SCALE_MS_SPACE, v)) {
							currentClosureCounts.addMsPopped();
							if(traceNarrow) {
								narrowRef = VM.objectModel.getKathy(v);
								if (ObjectReference.nullReference() != narrowRef) {
									if (cleanNarrow) {
										VM.objectModel.setKathy(v, ObjectReference.nullReference());
									} else {
										// Log.writeln("Tracing non null narrow reference");
										// Log.flush();
										currentClosureCounts.addNarrow();
										traceObject(narrowRef);
									}
								}
							}
						}
						scanObject(v);
					}
					processRememberedSets();
				} while (!toEmpty.isEmpty());
				super.assertMutatorRemsetsFlushed();
				// printClosureCounts("completeMetrixTrace - after processing values");
			}
			processRememberedSets();
		} while (!values.isEmpty());
		super.assertMutatorRemsetsFlushed();
	
		if(Options.scaleDebug.getValue() > 3) {
			currentClosureCounts.endClosure();
			Log.writeln("completeMetrixTrace - done");
			Log.flush();
		}
	}

	private boolean printObjects = false;
	protected void copyValues() {
		ObjectReference v;
		long msObjects = 0;
		if (!this.toEmpty.isEmpty()) {
			logMessage("ERROR: toEmpty is NOT empty before copying");
		}
		if(Options.scaleDebug.getValue() > 5) {
			logMessage("copyValues enter");
		}
		
		do {
			while (!values.isEmpty()) {
				v = values.pop();
				if (Space.isInSpace(Scale.SCALE_MS_SPACE, v)) {
					msObjects++;
				}
				if(printObjects && Options.scaleDebug.getValue() > 3) {
					VM.objectModel.dumpObject(v);
				}
				this.toEmpty.push(v);
			}
		} while(!values.isEmpty());

		currentClosureCounts.addDepth(msObjects);
		if(Options.scaleDebug.getValue() > 1) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write("Depth "); Log.write(currentClosureCounts.getDepth());
			Log.write(" copied msObjects "); Log.write(msObjects);
			Log.write(" msPoppedObjects ");Log.write(currentClosureCounts.getMsPopped());
			Log.write(" msPushedObjects ");Log.writeln(currentClosureCounts.getMsPushed());
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
		if(Options.scaleDebug.getValue() > 0) {
			if(!printObjects && msObjects < 10) {
				printObjects = true;
			} 
		}	
	}
	

	@Inline
	private void handlePopped(ObjectReference v, boolean cleanNarrow) {
		ObjectReference narrowRef;
		if (!VM.objectModel.validRef(v)) {
			logMessage("handlePopped - ERROR popped object not valid, skipping\n");
			return;
		}
		currentClosureCounts.addPopped(v);
		if (Space.isInSpace(Scale.SCALE_MS_SPACE, v)) {
			currentClosureCounts.addMsPopped();
			narrowRef = VM.objectModel.getKathy(v);
			if (ObjectReference.nullReference() != narrowRef) {
				if (cleanNarrow) {
					VM.objectModel.setKathy(v, ObjectReference.nullReference());
				} else {
					currentClosureCounts.addNarrow();
					traceObject(narrowRef);
				}
			}
		}
		scanObject(v);
	}

	@Inline
	protected void logMessage(String message) {
		if (Options.scaleDebug.getValue() > 3) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write(" - ScaleTraceLocal, name=");
			Log.write(name);
			Log.write(" - ");
			Log.writeln(message);
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
	}

	@Inline
	protected void logMessage(String message, ObjectReference object) {
		if (Options.scaleDebug.getValue() > 1) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write(" - ");
			Log.write(message);
			Log.writeln(object);
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
	}

	@Inline
	protected void logMessage(String message, long number) {
		if (Options.scaleDebug.getValue() > 3) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write(" - ");
			Log.write(message);
			Log.writeln(number);
			Log.flush();
			ScaleLogger.releaseLogLock();
		}
	}
	
	public void savePreClosureState(boolean save) {
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\tsavePreClosureState - enter");
		}
		this.currentClosureCounts.init();
		markAndSaveValues(save);
		if (!rootLocations.isEmpty()) {
			processRoots(save);
		}
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\tsavePreClosureState - exit");
		}
	}
	
	// need to mark for we have cleaned the mark bits
	private void markAndSaveValues(boolean save) {
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\tmarkAndSaveValues - enter");
		}

		long numSavedValues = 0;
		long restoredValues = 0;
		long invalid1 = 0;
		long invalid2 = 0;

		// all the values are popped and saved in 2 structures:
		// temporarily in toEmpty and
		// till the next closure in savedValues
		while (!values.isEmpty()) {
			ObjectReference v = this.values.pop();
			if (!VM.objectModel.validRef(v)) {
				logMessage("\tmarkAndSaveValues - popped object not valid, skipping");
				invalid1++;
				continue;
			}
			if (save) {
				savedValues.push(v);
				numSavedValues++;
			}
			toEmpty.push(v);
		}
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\tmarkAndSaveValues - values structure emptied");
		}

		// now values are restored from toEmpty
		while (!toEmpty.isEmpty()) {
			ObjectReference v = toEmpty.pop();
			if (!VM.objectModel.validRef(v)) {
				logMessage("\tmarkAndSaveValues - popped from toEmpty object not valid, skipping");
				invalid2++;
				continue;
			}

			if (isLive(v)) {
				processNode(v);
			} else {
				traceObject(v);
			}
			restoredValues++;
		}
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\tmarkAndSaveValues - toEmpty structure emptied");
		}

		if (Options.scaleDebug.getValue() > 5) {
			ScaleLogger.acquireLogLock();
			Log.prependThreadId();
			Log.write("\tmarkAndSaveValues - done, savedValues=");
			Log.write(numSavedValues);
			Log.write(" restoredValues=");
			Log.write(restoredValues);
			Log.write(" invalid in values=");
			Log.write(invalid1);
			Log.write(" invalid in toEmpty=");
			Log.writeln(invalid2);
			Log.flush();
			ScaleLogger.releaseLogLock();
			printClosureCounts("After markAndSaveValues ");	
		}

		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\tmarkAndSaveValues - exit");
		}
	}
	
	public void restorePreClosureState() {
		restoreSavedRoots();
		restoreSavedValues();
	}
	
	private void restoreSavedRoots() {
		long numRestoredRoots = 0;
		while (!this.savedRootLocations.isEmpty()) {
			Address entry = this.savedRootLocations.pop();
			this.rootLocations.push(entry);
			numRestoredRoots++;
		}
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\trestoreStoredRoots - done, numRestoredRoots=",
					numRestoredRoots);
		}
	}

	private void restoreSavedValues() {
		long numRestoredValues = 0;
		while (!savedValues.isEmpty()) {
			ObjectReference v = savedValues.pop();
			if (isLive(v)) {
				processNode(v);
			} else {
				traceObject(v);
			}
			numRestoredValues++;
		}
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("\trestoreSavedValues - done, numRestoredValues=",
					numRestoredValues);
			printClosureCounts("After restoreSavedValues");
		}
	}
	
	void makePrivateRootsPublic(AddressDeque globalRootLocations, ObjectReferenceDeque globalValues) {
		if (Options.scaleDebug.getValue() > 5) {
			logMessage("makePrivateRootsPublic - enter");
		}

		if (values.isNonEmpty()) {
			if (Options.scaleDebug.getValue() > 5) {
				logMessage("makePrivateRootsPublic - globalize values");
			}
			int numValues = 0;
			while(values.isNonEmpty()) {
				ObjectReference object = values.pop();
				globalValues.push(object);
				numValues++;
			}
			if (Options.scaleDebug.getValue() > 5) {
				ScaleLogger.acquireLogLock();
				Log.prependThreadId();
				Log.write(" ScaleTraceLocal.makePrivateRootsPublic - done, numValues=");
				Log.writeln(numValues);
				Log.flush();
				ScaleLogger.releaseLogLock();
			}
		}

		if (rootLocations.isNonEmpty()) {
			if (Options.scaleDebug.getValue() > 5) {
				logMessage("makePrivateRootsPublic - globalize rootLocations");
			}
			int numRoots = 0;
			while(rootLocations.isNonEmpty()) {
				Address addr = rootLocations.pop();
				globalRootLocations.push(addr);
				numRoots++;
			}
			if (Options.scaleDebug.getValue() > 5) {
				ScaleLogger.acquireLogLock();
				Log.prependThreadId();
				Log.write(" ScaleTraceLocal.makePrivateRootsPublic - done, numRoots=");
				Log.writeln(numRoots);
				Log.flush();
				ScaleLogger.releaseLogLock();
			}
		}

		if (Options.scaleDebug.getValue() > 5) {
			logMessage("makePrivateRootsPublic - exit");
		}
	}
}
